/** CS2340 Spring 2011 Project
 *  Team: 40 Jiggawatt Hard Driver
 *  Suk Hwan Hong
 *  Rhys Saraceni
 *  Travis Cooper
 *  Luke Stubb
 *
 * @author 40 Jiggawatt Hard Driver
 * @version 1.0 
 */

// $codepro.audit.disable com.instantiations.assist.eclipse.analysis.audit.rule.effectivejava.alwaysOverridetoString.alwaysOverrideToString
package edu.gatech.jiggawatt.office.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Represents Database
 * @author 40 Jiggawatt Hard Driver
 * @version 1.0 
 */
public class Database implements Serializable {
	
	private List<Admin> admins; // $codepro.audit.disable variableShouldBeFinal
	private List<Patient> patients;
	private List<Nurse> nurses;
	private List<Doctor> doctors;
	
	private int numAdmins = 0;
	private int numPatients = 0;	
	private int numNurses = 0;
	private int numDoctors = 0;
	
	private OfficeCalendar officeCalendar;
	
	private final int ADMIN_TYPE = 0, PATIENT_TYPE = 1, // $codepro.audit.disable instanceFieldNamingConvention, nonBlankFinalInstanceField
	NURSE_TYPE = 2, DOCTOR_TYPE = 3, ACCOUNT_LOCKED = 4; // $codepro.audit.disable instanceFieldNamingConvention, nonBlankFinalInstanceField, numericLiterals, unusedField
		
	/**
	 * Constructor for Database.
	 */
	public Database() {
		admins = new ArrayList<Admin>();
		patients = new ArrayList<Patient>();
		nurses = new ArrayList<Nurse>();
		doctors = new ArrayList<Doctor>();
		officeCalendar = new OfficeCalendar(5); // $codepro.audit.disable numericLiterals
	}
	
	/**
	 * adds appointment to database
	 * @param app appointment to add
	 * @return true if success, false otherwise
	 */
	public boolean addAppointment(Appointment app) // $codepro.audit.disable booleanMethodNamingConvention
	{
		if (officeCalendar.addAppointment(app))
		{
			((Patient) getUser(app.getId())).addAppointment(app);
			return true;
		}
		else
		{
			return false;
		}
	}
	
	/**
	 * removes appointment
	 * @param date date of appointment to remove
	 * @param time time of appointment to remove
	 * @return true if success, false otherwise
	 */
	public boolean removeAppointment(Date date, Time time) // $codepro.audit.disable booleanMethodNamingConvention
	{
		Appointment removedAppointment = officeCalendar.removeAppointment(date, time);
		if (null != removedAppointment)
		{
			return ((Patient) getUser(removedAppointment.getId())).removeAppointment(removedAppointment);
		}
		else
		{
			return false;
		}
	}
	
	/**
	 * changes appointment
	 * @param oldApp oldappointment to change
	 * @param newApp newappointment that contains new information
	 * @return true if success, false otherwise
	 */
	public boolean changeAppointment(Appointment oldApp, Appointment newApp) // $codepro.audit.disable booleanMethodNamingConvention
	{
		if (officeCalendar.changeAppointment(oldApp, newApp))
		{
			return ((Patient)getUser(oldApp.getId())).changeAppointment(oldApp, newApp);
		}
		else
		{
			return false;
		}
	}
	
	/**
	 * gets appointment
	 * @param date
	 * @param time
	 * @return appointment at that date and time (null if none exists)
	 */
	public Appointment getAppointment(Date date, Time time)
	{		
		return officeCalendar.getAppointment(date, time);
	}
	
	/**
	 * adds treatment record
	 * @param id
	 * @param tr
	 * @return true if success, false otherwise
	 */
	public boolean addTreatmentRecord(String id, TreatmentRecord tr) // $codepro.audit.disable booleanMethodNamingConvention
	{
		((Patient) getUser(id)).addTreatmentRecord(tr);
		return true;
	}
	
	/**
	 * changes treatment record
	 * @param id
	 * @param oldTr
	 * @param newTr
	 * @return true if success, false otherwise
	 */
	public boolean changeTreatmentRecord(String id, TreatmentRecord oldTr, TreatmentRecord newTr) // $codepro.audit.disable booleanMethodNamingConvention
	{
		return ((Patient) getUser(id)).changeTreatmentRecord(oldTr, newTr);
	}
	
	/**
	 * removes treatment record
	 * @param id
	 * @param tr
	 * @return true if success, false otherwise
	 */
	public boolean removeTreatmentRecord(String id, TreatmentRecord tr) // $codepro.audit.disable booleanMethodNamingConvention
	{
		return ((Patient) getUser(id)).removeTreatmentRecord(tr);
	}
	
	/**
	 * gets treatment record
	 * @param id
	 * @param date
	 * @param time
	 * @return treatment record retrieved
	 */
	public TreatmentRecord getTreatmentRecord(String id, Date date, Time time)
	{
		return ((Patient) getUser(id)).getTreatmentRecord(date, time);
	}
	
	/**
	 * gets treatment record lists
	 * @param id
	 * @return treatment record lists
	 */
	public List<TreatmentRecord> getTreatmentRecords(String id)
	{
		return ((Patient) getUser(id)).getTreatmentRecords();
	}
	
	/**
	 * sets doctors order
	 * @param id
	 * @param date
	 * @param time
	 * @param order
	 * @return true if success, false otherwise
	 */
	public boolean setDoctorsOrder(String id, Date date, Time time, DoctorsOrder order) // $codepro.audit.disable booleanMethodNamingConvention
	{
		getTreatmentRecord(id,date,time).setDoctorsOrder(order);
		return true;
	}
	
	/**
	 * returns list of yearly schedules
	 * @return list of yearly schedules
	 */
	public YearlySchedule[] getYearlySchedules()
	{
		return officeCalendar.getYearlySchedules();
	}
	
	/**
	 * returns list of daily schedules
	 * @param date
	 * @return list of daily schedules
	 */
	public DailySchedule getDailySchedule(Date date)
	{
		return officeCalendar.getDailySchedule(date);
	}
	
	
	/**
	 * add user
	 * @param user User
	 * @return true if success, false otherwise
	 */
	public boolean addUser(User user) { // $codepro.audit.disable booleanMethodNamingConvention
		
		if (null != getUser(user.getId()))
		{
			return false;
		}
		if (user.getUserType() == ADMIN_TYPE)
		{
			admins.add((Admin) user);
			numAdmins++;
			return true;
		}
		else if (user.getUserType() == PATIENT_TYPE)
		{
			patients.add((Patient) user);
			numPatients++;
			return true;
		}
		else if (user.getUserType() == NURSE_TYPE)
		{
			nurses.add((Nurse) user);
			numNurses++;
			return true;
		}
		else if (user.getUserType() == DOCTOR_TYPE)
		{
			doctors.add((Doctor) user);
			numDoctors++;
			return true;
		}
		else
		{
			return false;
		}
	}
	
	/**
	 * gets user
	 * @param id String
	 * @return User retrieved
	 */
	public User getUser(String id) {
		for (User e : admins) {
			if (e.getId().equals(id)) {
				return e;
			}
		}
		
		for (User e : patients) {
			if (e.getId().equals(id)) {
				return e;
			}
		}
		
		for (User e : nurses) {
			if (e.getId().equals(id)) {
				return e;
			}
		}
		
		for (User e : doctors) {
			if (e.getId().equals(id)) {
				return e;
			}
		}
		
		return null;
	}
	
	/**
	 * sets password of user
	 * @param id
	 * @param newPassword
	 * @return true if success, false otherwise
	 */
	public boolean setPassword(String id, String newPassword) // $codepro.audit.disable booleanMethodNamingConvention
	{
		if (null == getUser(id))
		{
			return false;
		}
		else
		{
			User oldUser = getUser(id);
			oldUser.setPassword(newPassword);
			return true;
		}
	}
	
	/**
	 * updates user
	 * @param updatedUser
	 * @return true if success, false otherwise
	 */
	public boolean updateUser(User updatedUser) { // $codepro.audit.disable booleanMethodNamingConvention
		if ((null == getUser(updatedUser.getId())) || (getUser(updatedUser.getId()).getUserType() != updatedUser.getUserType()))
		{
			return false;
		}
		else
		{
			User oldUser = getUser(updatedUser.getId());
			
			oldUser.setFirstName(updatedUser.getFirstName());
			oldUser.setLastName(updatedUser.getLastName());
			oldUser.setAge(updatedUser.getAge());
			oldUser.setGender(updatedUser.getGender());
			oldUser.setAddress(updatedUser.getAddress());
			oldUser.setPhone(updatedUser.getPhone());
			oldUser.setEmail(updatedUser.getEmail());
			oldUser.setSuspended(updatedUser.getSuspended());
			
			if (oldUser.getUserType() == PATIENT_TYPE)
			{
				((Patient) oldUser).setPharmacy(((Patient) updatedUser).getPharmacy());
				((Patient) oldUser).setInsurance(((Patient) updatedUser).getInsurance());
				((Patient) oldUser).setAllergies(((Patient) updatedUser).getAllergies());
			}
			
			return true;
		}
			
	}
	
	/**
	 * gets list of admins
	 * @return list of admins
	 */
	public List<Admin> getAdminList() {
		return admins;
	}
	
	/**
	 * gets list of patients
	 * @return list of patients
	 */
	public List<Patient> getPatientList() {
		return patients;
	}
	
	/**
	 * gets list of nurses
	 * @return list of nurses
	 */
	public List<Nurse> getNurseList() {
		return nurses;
	}
	
	/**
	 * gets list of doctors
	 * @return list of doctors
	 */
	public List<Doctor> getDoctorList() {
		return doctors;
	}
	
	/**
	 * gets patient
	 * @param fullName String
	 * @return Patient retrieved
	 */
	public Patient getPatient(String fullName) {
		for (Patient e : patients) {
			if (e.getFullName().equals(fullName)) {
				return e;
			}
		}
		return null;
	}
	
	/**
	 * removes user
	 * @param id String
	 * @return true if success, false otherwise
	 */
	public boolean removeUser(String id) { // $codepro.audit.disable booleanMethodNamingConvention
		for (Admin e : admins) {
			if (e.getId().equals(id)) {
			    //admins.remove(e);
				//Admin CANNOT BE REMOVED
				return false;
			}
		}
		
		for (Patient e : patients) {
			if (e.getId().equals(id)) {
			    patients.remove(e);
			    numPatients--;
				return true;
			}
		}
		
		for (Nurse e : nurses) {
			if (e.getId().equals(id)) {
			    nurses.remove(e);
			    numNurses--;
				return true;
			}
		}
		
		for (Doctor e : doctors) {
			if (e.getId().equals(id)) {
			    doctors.remove(e);
			    numDoctors--;
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * gets income statistic of specifies year and month
	 * @param year
	 * @param month
	 * @return income statistic of specifies year and month
	 */
	public IncomeStatistics getIncomeStatistics(int year, int month)
	{
		int patientsSeen = 0;
		double totalAmount = 0;
		
		for (Patient p : patients)
		{
			for (TreatmentRecord tr : p.getTreatmentRecords())
			{
				if (tr.getDate().getYear() == year && tr.getDate().getMonth() == month)
				{
					patientsSeen++;
					totalAmount += tr.calculateTotal();
				}
			}
		}
		
		return new IncomeStatistics(patientsSeen, totalAmount);
	}
	
	/**
	 * gets base year of office calendar
	 * @return base year of office calendar
	 */
	public int getBaseYear()
	{
		return officeCalendar.getBaseYear();
	}
	
	/**
	 * gets max year of office calendar
	 * @return max year of office calendar
	 */
	public int getMaxYear()
	{
		return officeCalendar.getMaxYear();
	}
	
	/**
	 * gets number of admins
	 * @return number of admins
	 */
	public int getNumAdmins()
	{
		return numAdmins;
	}
	
	/**
	 * gets number of patients
	 * @return number of patients
	 */
	public int getNumPatients()
	{
		return numPatients;
	}
	
	/**
	 * gets number of nurses
	 * @return number of nurses
	 */
	public int getNumNurses()
	{
		return numNurses;
	}
	
	/**
	 * gets number of doctors
	 * @return number of doctors
	 */
	public int getNumDoctors()
	{
		return numDoctors;
	}
	
	/**
	 * gets number of total users
	 * @return number of total users
	 */
	public int getNumUsers()
	{
		return numAdmins + numPatients + numNurses + numDoctors;
	}	
}
